﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Web;

namespace CustomSearchCondition
{

    public static class ExpressionExtension
    {

        public static Expression<Func<T, bool>> True<T>()
        {
            return (T f) => true;
        }

        public static Expression<Func<T, bool>> False<T>()
        {
            return (T f) => false;
        }
        public static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge)
        {
            var map = first.Parameters.Select((f, i) => new { f, s = second.Parameters[i] }).ToDictionary(p => p.s, p => p.f);
            var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);
            return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
        }

        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
        {
            return first.Compose(second, Expression.And);
        }

        public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
        {
            return first.Compose(second, Expression.Or);
        }


        /// <summary>
        /// 根据字段、操作符及输入值生成动态表达式
        /// </summary>
        /// <typeparam name="T">表达式实体</typeparam>
        /// <param name="fieldName">字段名称</param>
        /// <param name="operation">操作符</param>
        /// <param name="value">输入值</param>
        /// <returns></returns>
        public static Expression<Func<T, bool>> BuildDynamicExpression<T>(this Expression<Func<T, bool>> exp, string fieldName, string operation, string value)
        {
            if (string.IsNullOrWhiteSpace(fieldName) || string.IsNullOrWhiteSpace(operation) || string.IsNullOrWhiteSpace(value))
            {
                throw new ArgumentNullException();
            }
            try
            {
                //构建Lambda表达式
                ParameterExpression parameter = Expression.Parameter(typeof(T), "p");
                Expression constant = null;
                //表达式左侧 like: p.Name
                var left = Expression.PropertyOrField(parameter, fieldName);
                var fieldType = left.Type.Name;
                object val = value;
                if (fieldType.Equals("Int32"))
                {
                    val = Convert.ToInt32(value);
                }
                else if (fieldType.Equals("DateTime"))
                {
                    val = Convert.ToDateTime(value);
                }
                //表达式右侧，比较值， like '张三'
                var right = Expression.Constant(val);
                //比较表达式
                switch (operation)
                {
                    case "include"://包含
                        {
                            //like 查询，需要调用外部int或string的Contains方法
                            var method = typeof(string).GetMethod("Contains", new Type[] { typeof(string) });
                            constant = Expression.Call(left, method, right);
                            break;
                        }
                    case "notinclude"://不包含
                        {
                            var containsMethod = typeof(string).GetMethod("Contains", new Type[] { typeof(string) });
                            var mexpress = Expression.Call(left, containsMethod, right);
                            constant = Expression.Not(mexpress);
                            break;
                        }
                    case "gt"://大于
                        constant = Expression.GreaterThan(left, right);
                        break;
                    case "ge"://大于等于
                        constant = Expression.GreaterThanOrEqual(left, right);
                        break;
                    case "lt"://小于
                        constant = Expression.LessThan(left, right);
                        break;
                    case "le"://小于等于
                        constant = Expression.LessThanOrEqual(left, right);
                        break;
                    case "ne"://不等于
                        constant = Expression.NotEqual(left, right);
                        break;
                    case "eq"://等于
                        constant = Expression.Equal(left, right);
                        break;
                    default:
                        break;
                }
                if (constant != null)
                {
                    var expression = Expression.Lambda<Func<T, Boolean>>(constant, parameter);
                    return expression;
                }
            }
            catch (Exception ex)
            {

                throw ex;
            }
            return null;
        }


    }
    public class ParameterRebinder : ExpressionVisitor
    {
        private readonly Dictionary<ParameterExpression, ParameterExpression> Map;

        public ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> _Map)
        {
            this.Map = (_Map ?? new Dictionary<ParameterExpression, ParameterExpression>());
        }

        public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> _Map, Expression _Exp)
        {
            return new ParameterRebinder(_Map).Visit(_Exp);
        }

        protected override Expression VisitParameter(ParameterExpression _PExp)
        {
            ParameterExpression parameterExpression = default(ParameterExpression);
            if (this.Map.TryGetValue(_PExp, out parameterExpression))
            {
                _PExp = parameterExpression;
            }
            return base.VisitParameter(_PExp);
        }
    }
}